/*
 * ==================== vector add ====================
 * parameters:
 * .width : total width of input : 8/16/32/64/...
 * .granu : the granularity of input : 8/16/32/64/...
 *  
 * input/output width:
 * o_sum : width
 * o_carrys : with/granu
 * i_a/i_b : width
 * i_mode : log2(width/granu)+1
 * 
 * for example:
 * ------------------------------------------------------------
 * add_vector #(.width(64), 
			 .granu(8)) U_add_vector (
   									  // Outputs
   									  .o_sum			(sum[63:0]),
   									  .o_carrys			(carrys[7:0]),
   									  // Inputs
   									  .i_a				(a[63:0]),
   									  .i_b				(b[63:0]),
   									  .i_mode			(mode[3:0]) );
 * ------------------------------------------------------------
 * implements:
 * one 64+64 or two 32+32 or four 16+16 or eight 8+8
*/

module add_vector(/*AUTOARG*/
   // Outputs
   o_sum, o_carrys,
   // Inputs
   i_a, i_b, i_mode
   );
   
   parameter width = 64;		// total width
   parameter granu = 8;			// granularity(8/16/32/64)
   localparam fields_num = width / granu;
   localparam mode_num = log2(fields_num) + 1; // the num of mode

   output logic [width-1:0] o_sum;
   output logic [fields_num-1:0] o_carrys;
   
   input logic [fields_num-1:0][granu-1:0] i_a, i_b;	// the addend
   input logic [mode_num-1:0] 			   i_mode;
   

   logic [fields_num-1:0][granu-1:0] op_a, op_b;
   logic [fields_num-1:0] 			 dig_hole_cond;

   always_comb
	 begin
		for(int i=0;i<fields_num;i++)
		  begin
			 dig_hole_cond[i] = 1'b0;
			 for(int j=0;j<mode_num;j++)
			   if((i+1)%(2**j) == 0)
				 dig_hole_cond[i] |= i_mode[j];
		  end
	 end

   always_comb
	 begin
		for(int i=0;i<fields_num;i++)
		  begin
			 // dig the MSB of each mode as a hole
			 op_a[i] = { i_a[i][granu-1] & ~dig_hole_cond[i], i_a[i][granu-2:0] };
			 op_b[i] = { i_b[i][granu-1] & ~dig_hole_cond[i], i_b[i][granu-2:0] };
		  end
	 end


   logic [fields_num-1:0][granu-1:0] sum_with_holes;
   DW01_add #(.width(width)) U_add(
   								   .A(op_a),
   								   .B(op_b),
   								   .CI(1'b0),
   								   .SUM(sum_with_holes),
   								   .CO() ); // infact, this Carry is for all mode
   
   
   logic [fields_num-1:0] holes, carrys;
   generate
	  genvar 	i;
	  for(i=0;i<fields_num;i++)
		begin : gen_holes_carrys
		   DW01_add #(.width(1)) U_holes_add(
   											 .A(i_a[i][granu-1]),
   											 .B(i_b[i][granu-1]),
   											 .CI(sum_with_holes[i][granu-1]),
   											 .SUM(holes[i]),
   											 .CO(carrys[i]) );
		end
   endgenerate

   logic [fields_num-1:0][granu-1:0] sum_final;
   always_comb
	 begin
		for(int i=0;i<fields_num;i++)
		  begin
			 sum_final[i] = { dig_hole_cond[i] ? holes[i] : sum_with_holes[i][granu-1], sum_with_holes[i][granu-2:0] };
		  end
	 end
   
   assign o_sum = sum_final;
   assign o_carrys = carrys;
   
endmodule // add_vector
